package com.fouram.web.aop;

import cn.hutool.json.JSONUtil;
import com.fouram.core.base.BaseBo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 开放接口（RPC或REST API） 服务端统一处理切面
 * - 入参校验：接口请求入参校验
 * - 日志记录：成功或失败
 */
@Aspect
@Component
@Order(0)
@Slf4j
@AllArgsConstructor
public class OpenApiAspect {

    @Pointcut(value = "execution(* com.fouram..controller..*.*(..))")
    public void apiPointcut() {
    }

    @Around("apiPointcut()")
    public Object doApi(ProceedingJoinPoint joinPoint) throws Throwable {
        LocalDateTime startTime = LocalDateTime.now();
        if (joinPoint.getArgs().length > 0 && joinPoint.getArgs()[0] instanceof BaseBo) {
            BaseBo request = (BaseBo) joinPoint.getArgs()[0];
            try {
                // 接口入参校验
                request.checkInput();
                // 接口业务处理
                Object response = joinPoint.proceed();
                // 成功日志
                okLog(startTime, response, request);
                // 成功应答
                return response;
            } catch (Throwable ex) {
                // 失败日志
                failLog(startTime, ex, request);
                throw ex;
            }
        } else {
            try {
                // 接口业务处理
                Object response = joinPoint.proceed();
                // 成功应答
                return  response;
            } catch (Throwable ex) {
                throw ex;
            }
        }
    }

    private void okLog(LocalDateTime startTime, Object response, BaseBo request) {
        log(startTime, null, response, request);
    }

    private void failLog(LocalDateTime startTime, Throwable e, BaseBo request) {
        log(startTime, e, null, request);
    }

    private void log(LocalDateTime startTime, Throwable e, Object response, BaseBo request) {
        log.info("入参: {}\n{}", startTime, JSONUtil.toJsonPrettyStr(request));
        if (e == null) {
            log.info("出参: {}\n{}", LocalDateTime.now(), JSONUtil.toJsonPrettyStr(response));
        } else {
            log.error("报错: {}\n{}", LocalDateTime.now(), e);
        }
    }
}
